package com.asdhawaii.otello;

import com.asdhawaii.otello.board.BoardPanel;
import com.asdhawaii.otello.board.SquareContents;
import com.asdhawaii.otello.players.AbstractOtelloAi;
import com.asdhawaii.otello.players.PlayerType;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;

/**
 * The panel for the Otello window.
 * 
 * This should include a BoardPanel (in CENTER) and information for the current round as well as
 * overall statistics (EAST).  The main problem is coordinating these two areas since they
 * depend on information in each other; for example, the BoardPanel currently contains the turn
 * number, which would be necessary to indicate whose move it is in the Statistics panel.  That 
 * might be a significant reason to place all of those areas into a single panel, such that an 
 * update to one will be able to influence the others.  Alternately, it might be possible for 
 * BoardPanel and the other sections to contain a reference to their container (this OtelloPanel),
 * in which case it might be possible to go from one section to OthelloPanel and then to another 
 * section.  Since the second solution seems more complicated, this developer will attempt it first.
 * 
 * For the purposes of this application, a "round" refers to a a single playthrough of Otello,
 * a "play" is the aggregate of all rounds in this program execution, and the application itself
 * is the "game."
 * 
 * @author Branden Ogata
 *
 */

@SuppressWarnings("serial")
public class OtelloPanel extends JPanel
{
  /**
   * The Otello board.
   * 
   */
  private BoardPanel board;
  
  /**
   * The statistics panel.
   * 
   */
  private StatsPanel stats;
  
  /**
   * The type of player for Player Zero.
   * 
   */
  private PlayerType playerZeroType;
  
  /**
   * The AI for Player Zero; if Player Zero is human, then null.
   * 
   */
  private AbstractOtelloAi playerZeroAi;
  
  /**
   * The name for Player Zero.
   * 
   */
  private String playerZeroName;
  
  /**
   * The type of player for Player One.
   * 
   */
  private PlayerType playerOneType;
  
  /**
   * The AI for Player One; if Player One is human, then null.
   * 
   */
  private AbstractOtelloAi playerOneAi;  
  
  /**
   * The name for Player One.
   * 
   */
  private String playerOneName;
  
  /**
   * The current turn number.
   * 
   */
  private int turn;  
  
  /**
   * Creates a new OtelloPanel.
   * 
   */
  
  public OtelloPanel()
  {
    this.playerZeroType = PlayerType.GIRL;
    this.playerZeroAi = null;
    this.playerZeroName = "Christine";
    this.playerOneType = PlayerType.BOY;
    this.playerOneAi = null;
    this.playerOneName = "Branden";
    
    // The board
    this.board = new BoardPanel(this);
    this.add(board, BorderLayout.CENTER);
    
    // The statistics for the current round and game
    this.stats = new StatsPanel(this);
    this.add(stats, BorderLayout.EAST);    
    
    this.turn = 0;    
    
    this.setVisible(true);
  }
  
  /**
   * Returns the type of player for Player Zero of this OtelloPanel.
   *
   * @return playerZeroType The PlayerType containing the type of Player Zero for this OtelloPanel.
   *
   */
  
  public PlayerType getPlayerZeroType()
  {
    return this.playerZeroType;
  }

  /**
   * Sets the type of Player Zero for this OtelloPanel.
   *
   * @param playerZeroType The PlayerType containing the type to set for Player Zero.
   *
   */
  
  public void setPlayerZeroType(PlayerType playerZeroType)
  {
    this.playerZeroType = playerZeroType;
  }

  /**
   * Returns the AI for Player Zero of this OtelloPanel.
   *
   * @return playerZeroAi The OtelloAi containing the AI for Player Zero of this OtelloPanel.
   *
   */
  
  public AbstractOtelloAi getPlayerZeroAi()
  {
    return this.playerZeroAi;
  }

  /**
   * Sets the AI for Player Zero of this OtelloPanel.
   *
   * @param playerZeroAi The OtelloAi containing the AI to set for Player Zero.
   *
   */
  
  public void setPlayerZeroAi(AbstractOtelloAi playerZeroAi)
  {
    this.playerZeroAi = playerZeroAi;
  }

  /**
   * Returns the name for Player Zero of this OtelloPanel.
   *
   * @return playerZeroName The String containing the name for Player Zero of this OtelloPanel.
   *
   */
  
  public String getPlayerZeroName()
  {
    return this.playerZeroName;
  }

  /**
   * Sets the name for Player Zero of this OtelloPanel.
   *
   * @param playerZeroName The String containing the name to set for Player Zero.
   *
   */
  
  public void setPlayerZeroName(String playerZeroName)
  {
    this.playerZeroName = playerZeroName;
  }

  /**
   * Returns the type of player for Player Zero of this OtelloPanel.
   *
   * @return playerOneType The PlayerType containing the type for Player Zero of this OtelloPanel.
   *
   */
  
  public PlayerType getPlayerOneType()
  {
    return this.playerOneType;
  }

  /**
   * Sets the type for Player One of this OtelloPanel.
   *
   * @param playerOneType The PlayerType containing the type to set for Player One.
   *
   */
  
  public void setPlayerOneType(PlayerType playerOneType)
  {
    this.playerOneType = playerOneType;
  }

  /**
   * Returns the AI for Player One of this OtelloPanel.
   *
   * @return playerOneAi The OtelloAi containing the AI for Player One of this OtelloPanel.
   *
   */
  
  public AbstractOtelloAi getPlayerOneAi()
  {
    return this.playerOneAi;
  }

  /**
   * Sets the AI for Player One of this OtelloPanel.
   *
   * @param playerOneAi The OtelloAi containing the AI to set for Player One.
   *
   */
  
  public void setPlayerOneAi(AbstractOtelloAi playerOneAi)
  {
    this.playerOneAi = playerOneAi;
  }

  /**
   * Returns the name for Player One of this OtelloPanel.
   *
   * @return playerOneName The String containing the name for Player One of this OtelloPanel.
   *
   */
  
  public String getPlayerOneName()
  {
    return this.playerOneName;
  }

  /**
   * Sets the name for Player One of this OtelloPanel.
   *
   * @param playerOneName The String containing the name to set for Player One.
   *
   */
  
  public void setPlayerOneName(String playerOneName)
  {
    this.playerOneName = playerOneName;
  }

  /**
   * Returns the number of turns taken thus far on this BoardPanel.
   *
   * @return turn The int containing the number of turns taken thus far on this BoardPanel.
   *
   */
  
  public int getTurn()
  {
    return this.turn;
  }  
  
  /**
   * Increments the turn by one and updates the stats table row select.
   * 
   */
  
  public void nextTurn()
  {
    this.turn++;
    this.stats.updateRowSelection();
  }
  
  /**
   * Resets the game, including the board and table.
   * 
   */
  
  public void reset()
  {
    this.board.reset();
    this.stats.reset();
    resetTurn();
  }
  
  /**
   * Resets the turn to zero.
   * 
   */
  
  public void resetTurn()
  {
    this.turn = 0;
  }
  
  /**
   * Makes the move for the given player.
   * If the player specified in the parameters is a human, then exits without doing anything,
   * which should return control to the player.
   * 
   * @param player The SquareContents indicating the color of the player to move.
   * 
   */
  
  public void nextMove(final SquareContents player)
  {
    final AbstractOtelloAi ai;
    
    switch (player)
    {
      // If Black, then Player Zero
      case BLACK:
        ai = this.playerZeroAi;
        break;
      
      // If White, then Player One
      case WHITE:
        ai = this.playerOneAi;
        break;
        
      default:
        ai = null;
    }
    
    // If the player type is not a user
    if (ai != null)
    {
      Thread thread = new Thread(new Runnable() {
        public void run()
        {
/*          
          Timer.pauseForMillis(4096);
          for (int i = 0; i < board.getBoard().length; i++)
          {
            for (int j = 0; j < board.getBoard()[i].length; j++)
            {
              if (board.getBoard()[i][j].getFlipper() != null)
              {
                boolean animationDone = board.getBoard()[i][j].getFlipper().isDone();
                
                while (!animationDone)
                {
                  animationDone = board.getBoard()[i][j].getFlipper().isDone();
                  System.out.println(i + "," + j + " not done");
                }
              }
            }
          }
*/          
                    
          // Get the move that the AI would make
          String move = ai.getMove(board.getBoard(), board.validMoves(player));
          
          // Make the move on the board
          if (move != null)
          {
            int indices = Integer.valueOf(move);
            board.getBoard()[indices % 10][indices / 10].doClick();          
          }
        }
      });
      
/*      
      System.out.println(thread.getName());
*/
      thread.run();
/*
        nextMove(player.opposite());
        updateCount(this.board.getPieceCount(SquareContents.BLACK), 
                    this.board.getPieceCount(SquareContents.WHITE));
*/                    

//      nextTurn();
    }
  }
  
  /**
   * Updates the count of pieces on the board.
   * 
   * @param blackCount The int equal to the number of black pieces on the board.
   * @param whiteCount The int equal to the number of white pieces on the board.
   * 
   */
  
  public void updateCount(int blackCount, int whiteCount)
  {
    this.stats.updatePlayerPieces(0, blackCount);
    this.stats.updatePlayerPieces(1, whiteCount);
    
    // If there are no empty spaces or none of the empty spaces allow a valid move for either side,
    // then let the user know that the game is over and which player won (if either, else a draw)
    // For testing, use the moves: f5 f6 f7 f4 f3 e3 d3 c5 b5
    if ((blackCount + whiteCount == 64) || ((!this.board.isPlayable(SquareContents.BLACK)) && 
                                            (!this.board.isPlayable(SquareContents.WHITE))))
    {
      final JDialog endMessage = new JDialog();
      endMessage.setLocationRelativeTo(this);
      endMessage.setTitle("Game Over!");

      JPanel endPanel = new JPanel();
      endPanel.setLayout(new BoxLayout(endPanel, BoxLayout.LINE_AXIS));
      endPanel.setAlignmentX(JPanel.CENTER_ALIGNMENT);
      endPanel.add(Box.createHorizontalGlue());
      
      JPanel contentPanel = new JPanel();
      contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.PAGE_AXIS));
      JLabel endLabel = new JLabel((blackCount == whiteCount) ? 
                                   ("It is a draw!") : 
                                   ((blackCount > whiteCount) ? ("Black wins!") : 
                                                                ("White wins!")));
      endLabel.setHorizontalTextPosition(SwingConstants.CENTER);
      endLabel.setHorizontalAlignment(SwingConstants.CENTER);
      contentPanel.add(endLabel);
      JButton endConfirm = new JButton("OK");
      endConfirm.addActionListener(new ActionListener()
                                   {
                                     /**
                                      * Handles any button presses on the confirm button.
                                      * 
                                      * @param event The ActionEvent that recorded the click.
                                      * 
                                      */
        
                                     @Override
                                     public void actionPerformed(ActionEvent event)
                                     {
                                       endMessage.dispose();
                                     }
                                   });
      contentPanel.add(endConfirm);
      endPanel.add(contentPanel);
      endPanel.add(Box.createHorizontalGlue());
      
      endMessage.setContentPane(endPanel);
      endMessage.setModal(false);
      endMessage.pack();
      endMessage.setSize((int) endMessage.getSize().getWidth() * 2, 
                         (int) endMessage.getSize().getHeight() * 2);
      endMessage.setVisible(true);
    }
  }
}